{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "# Superdense Coding\n", "This is a port of [this notebook](https://github.com/QuTech-Delft/quantum-inspire-examples/blob/dev/docs/notebooks_action_needed/superdense_coding/superdense_coding.ipynb).\n", "\n", "In this notebook, we use the Quantum Inspire to implement the superdense coding algorithm. With this quantum communication protocol, a sender (Bob) can transmit two classical bits of information to a receiver (Alice) using only one qubit. In addition, this method of communication is highly secure against eavesdropping since reading out the state during the communication will collapse the entangled state, indicating the presence of a third party listening.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2024-11-20T12:37:04.536147800Z", "start_time": "2024-11-20T12:37:04.456110900Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "from qiskit import QuantumCircuit\n", "\n", "from qiskit_quantuminspire.qi_provider import QIProvider" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We divide the superdense code into four main sections: initilization, encoding, decoding, measurement. Initially, Alice creates the entangled state and sends one qubit to Bob for the encoding step. After the encoding, Bob returns the qubit to Alice, who decodes the message and measures the two qubits.\n", "\n", "![Superdense Coding](super_dense_coding.png)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "### Generate the circuit\n", "\n", "The two qubits are initially prepared in the ground state 00. Before sending the qubit to Bob, Alice needs to prepare an entangled state. So, Alice applies a H-gate followed by a CNOT-gate to obtain the desired entangled state (1/sqrt(2))(00 + 11). Now the qubit is ready to be sent to Bob, who encodes the two classical bits of information.\n", "\n", "Bob can choose between four gates to apply to the qubit. Each gate will encode a different message for Alice. The four different possibilities are listed in the table below.\n", "\n", "| Gates\t | Classical Message |\n", "|:------:| :------: |\n", "| I\t | 00 |\n", "| X\t | 01 |\n", "| Z\t | 10 |\n", "| ZX | 11 |\n", "\n", "A different Bell state will be encoded for each gate that Bob applies to the qubit. After decoding, each Bell state will result in a different 2-bit message. Remember that the Identity gate doesn't alter the state, and the X and Z gates make the qubit do a pi-rotation over their respective axes.\n", "\n", "The different possible states after encoding will then be:\n", "\n", "![encoding states](encoding_states.png)\n", "\n", "After the encoding step, Bobs sends the qubit back to Alice. To decode the message, Alice applies a CNOT-gate followed by a H-gate, as shown in the first figure. The decoding of the '11' message is: H CNOT (1/sqrt(2))(-10 + 01) = 11" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "start_time": "2024-11-20T11:02:48.197216900Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "def superdense_encoding_circuit(bit_a: int = 0, bit_b: int = 0) -> QuantumCircuit:\n", " circuit = QuantumCircuit(2, 2)\n", " qubit_a = 0\n", " qubit_b = 1\n", " circuit.reset(qubit_a)\n", " circuit.reset(qubit_b)\n", " circuit.measure(qubit_a, qubit_a)\n", " circuit.measure(qubit_b, qubit_b)\n", " circuit.ry(1.57079632679, qubit_a)\n", " circuit.ry(-1.57079632679, qubit_b)\n", " circuit.cz(qubit_b, qubit_a)\n", "\n", " ## apply variable gate\n", " encode_gate = bit_a + 2**bit_b\n", " if encode_gate == 0: # identity I\n", " circuit.id(qubit_a)\n", " elif encode_gate == 1: # X\n", " circuit.x(qubit_a)\n", " elif encode_gate == 2: # Z\n", " circuit.z(qubit_a)\n", " elif encode_gate == 3: # Y\n", " circuit.y(qubit_a)\n", "\n", " circuit.cz(qubit_b, qubit_a)\n", " circuit.ry(-1.57079632679, qubit_a)\n", " circuit.ry(1.57079632679, qubit_b)\n", "\n", " # maximum-likelihood 3 readout (ML3 RO)\n", " circuit.measure(qubit_b, qubit_a) # in qiskit this measures in z basis\n", " circuit.measure(qubit_b, qubit_a) # in qiskit this measures in z basis\n", " circuit.measure(qubit_b, qubit_a) # in qiskit this measures in z basis\n", "\n", " return circuit" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "### Run the Circuit in Quantum Inspire Platform\n", "We generate a circuit for each of the possibilities (00, 01, 10, 11) and then run the circuit in the Quantum Inspire. We connect and choose one of the available backends." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "provider = QIProvider()\n", "backend = provider.get_backend(name=\"QX emulator\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We run a job for a certain amount of shots for each combination of classical bits we wish to send. We wait for each result and collect all." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result_map = {}\n", "for bit_a in range(2):\n", " for bit_b in range(2):\n", " circuit = superdense_encoding_circuit(bit_a, bit_b)\n", "\n", " # if using qxelarator\n", " # cqasm_string = dumps(circuit)\n", " # results = execute_string(cqasm_string, iterations=100).results\n", " # result_map[f'{bit_a}{bit_b}'] = results\n", "\n", " # if using thq QI2 Platform\n", " job = backend.run(circuit, shots=1024)\n", " results = job.result(wait_for_results=True)\n", " counts = results.data()[\"counts\"]\n", " result_counts = {}\n", " for key in counts:\n", " bin_str = format(int(key, 16), \"04b\")\n", " result_counts[bin_str] = counts[key]\n", "\n", " result_map[f\"{bit_a}{bit_b}\"] = result_counts" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "### Plot results\n", "We plot the results of each encoding using matplotlib" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2024-11-20T12:50:57.519324200Z", "start_time": "2024-11-20T12:50:57.462245200Z" }, "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig, axs = plt.subplots(2, 2, figsize=(8, 6))\n", "for index, key in enumerate(result_map):\n", " results = result_map[key]\n", " entries = list(results.keys())\n", " occurrences = list(results.values())\n", " i0 = int(index / 2)\n", " i1 = index % 2\n", " axs[i0][i1].bar(entries, occurrences, color=\"skyblue\")\n", " axs[i0][i1].set_title(f\"Supercoding {key}\")\n", " axs[i0][i1].set_xlabel(\"Entries\")\n", " axs[i0][i1].set_ylabel(\"Occurrences\")\n", "\n", "# Adjust layout to prevent overlap\n", "plt.tight_layout()\n", "\n", "# Display the plot\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 4 }